Skip to content

feat: QA bug hunt + hardened git hooks (SAST, AI attribution stripping)#31

Merged
e6qu merged 2 commits intodevfrom
qa/bug-hunt-e2e
Mar 22, 2026
Merged

feat: QA bug hunt + hardened git hooks (SAST, AI attribution stripping)#31
e6qu merged 2 commits intodevfrom
qa/bug-hunt-e2e

Conversation

@e6qu
Copy link
Copy Markdown
Owner

@e6qu e6qu commented Mar 22, 2026

QA Bug Hunt + Hardened Git Hooks

Console.log Cleanup (18 calls → Log.create)

All console.log/console.error in TUI production code replaced with structured logging via Log.create({ service: "tui" }). 9 files modified.

Code Quality Issues Documented (Q1-Q5)

# Issue Severity Status
Q1 95 empty .catch(() => {}) blocks Low Documented
Q2 17 TODO/FIXME/HACK comments Low Documented
Q3 console.log in TUI Low FIXED
Q4 Copilot SDK chunk type safety Med Documented
Q5 process.env vs Env.set Low Documented

Hardened Git Hooks

Pre-commit (.husky/pre-commit):

  1. Prettier autoformat (existing)
  2. NEW: scripts/strip-ai-attribution.sh — auto-strips AI tool watermarks from staged files
  3. NEW: scripts/sast-check.sh — lightweight SAST (no eval, no Function, no innerHTML, no hardcoded secrets, no console.log)
  4. Turbo typecheck (existing)
  5. Turbo test (existing)

Commit-msg (.husky/commit-msg):

  1. Conventional commit validation (existing)
  2. NEW: Auto-strips Co-authored-by trailers from 17 AI tools
  3. NEW: Strips Authored-by/Written-by/Generated-by AI markers

Pre-push (.husky/pre-push):

  1. Bun version check (existing)
  2. NEW: Verifies no AI attribution in commits being pushed (blocks with error)
  3. NEW: Strips AI attribution from PR body via gh pr edit (if PR exists)
  4. Turbo typecheck (existing)
  5. Turbo test (existing)

AI Tools Covered (17)

Claude, Copilot, Cursor, ChatGPT, OpenAI, Anthropic, Devin, Aider, Codeium, Tabnine, Windsurf, Cody, Continue, Augment, Supermaven + generic AI/Generated patterns.

SAST Checks

Check CWE Action
eval() CWE-95 Block
new Function() CWE-95 Block
innerHTML CWE-79 Warn
Hardcoded secrets CWE-798 Block
console.log in src/ Quality Warn

Add // sast-ignore to suppress false positives.

Documentation

Test plan

  • bun run typecheck (tsgo) — 0 errors
  • bun test — 1512 pass, 0 fail
  • SAST script tested manually — blocks eval(), warns on console.log
  • Attribution script tested — strips patterns from staged files

Adrian Mârza added 2 commits March 22, 2026 03:02
…nt 5 code quality issues

Console.log cleanup (9 TUI files):
- app.tsx: clipboard error, route data debug → log.error, log.debug
- context/theme.tsx: system theme resolution → log.debug
- context/route.tsx: navigation logging → log.debug
- context/sync.tsx: bootstrap logging → log.debug
- util/clipboard.ts: backend selection (6 calls) → log.debug
- component/dialog-mcp.tsx: MCP toggle errors → log.error
- component/prompt/index.tsx: session creation error → log.error
- component/dialog-workspace-list.tsx: workspace errors → log.error
- routes/session/index.tsx: sync error → log.error

Code quality issues documented in BUGS.md (Q1-Q5):
- Q1: 95 empty .catch(() => {}) blocks (most intentional, ~10 mask errors)
- Q2: 17 TODO/FIXME/HACK comments as tech debt
- Q3: console.log in TUI (FIXED in this PR)
- Q4: Copilot SDK lost chunk type safety (upstream TODO)
- Q5: process.env vs Env.set architectural issue
…eaning

Pre-commit additions:
- scripts/strip-ai-attribution.sh: auto-strips AI tool watermarks from
  staged files (17 tool patterns: Cursor, Copilot, Claude, ChatGPT,
  Devin, Aider, Codeium, Windsurf, Cody, Continue, Augment, Supermaven, etc.)
- scripts/sast-check.sh: lightweight SAST for staged TypeScript files
  (no eval, no Function(), no innerHTML, no hardcoded secrets, no console.log)
  Blocks on errors, warns on code quality issues. `// sast-ignore` to suppress.

Commit-msg additions:
- Auto-strips Co-authored-by trailers from 15+ AI tools
- Strips Authored-by/Written-by/Generated-by AI markers

Pre-push additions:
- Verifies no AI attribution in commits being pushed (blocks with error)
- Strips AI attribution from existing PR body via `gh pr edit` (if PR exists)

Documentation:
- docs/AI_ATTRIBUTION_POLICY.md: full list of stripped patterns, when/how
  stripping happens, how to add new patterns
@e6qu e6qu changed the title fix: QA bug hunt — console.log cleanup, code quality issues documented feat: QA bug hunt + hardened git hooks (SAST, AI attribution stripping) Mar 22, 2026
@e6qu e6qu merged commit 5a28cf3 into dev Mar 22, 2026
1 check passed
@e6qu e6qu deleted the qa/bug-hunt-e2e branch March 22, 2026 01:46
e6qu pushed a commit that referenced this pull request Mar 22, 2026
The merge of PR #31 lost the CI workflow updates and kept stale hook
patterns. This commit restores:

- CI workflow: SAST check + AI attribution check steps (with fetch-depth: 0)
- commit-msg hook: verified real patterns (Cursor, Claude, Aider, Copilot)
- pre-push hook: same verified patterns + PR body stripping
- pre-commit hook: removed staged file stripping (commit-msg only)
e6qu added a commit that referenced this pull request Mar 22, 2026
* fix: restore CI SAST/attribution steps and fix hooks after merge

The merge of PR #31 lost the CI workflow updates and kept stale hook
patterns. This commit restores:

- CI workflow: SAST check + AI attribution check steps (with fetch-depth: 0)
- commit-msg hook: verified real patterns (Cursor, Claude, Aider, Copilot)
- pre-push hook: same verified patterns + PR body stripping
- pre-commit hook: removed staged file stripping (commit-msg only)

* fix: 5 bugs (B53-B57) + CI SAST/attribution + tmux tests verified

Bug fixes (found via deep code analysis):
- B53: CAS.deleteBySession() race → Database.transaction()
- B54: CAS.deleteOrphans() shared entry deletion → EditGraphNode ref check
- B55: EditGraph.checkout() partial failure → Database.transaction()
- B56: EditGraph.deleteBySession() not atomic → Database.transaction()
- B57: filterEdited() synthetic ID collision → PartID.ascending()

False positives verified and documented (E2-E6):
- getHead() undefined vs null → correct TS idiom
- First commit branch init → intentional DAG initialization
- Objective.extract() concurrency → prompt loop serializes
- SideThread duplicate ID → fail loudly is correct
- SHA-256 collision → intentional (B43 fix)

CI workflow restored with SAST + AI attribution checks.
tmux TUI tests: all 3 flows pass (home, command-palette, agent-cycle).

* fix: add sast-ignore to debug agent Function() — pre-existing debug command
e6qu pushed a commit that referenced this pull request Mar 22, 2026
B58: pluginGuard() now catches Plugin.trigger() errors and returns
EditResult error instead of propagating uncaught exception.

B59: pluginNotify() now catches Plugin.trigger() errors and logs
warning instead of silently ignoring.

BUGS.md cleaned up:
- Removed duplicate "Open — Bugs (0)" section
- Added B58-B59 as fixed
- Added 10 new false positives from QA analysis (V1, R4, E1-fork,
  PM1, PM2, etc.) — each verified with reasoning
- Updated Q3 reference from "this PR" to "PR #31"
- Total: 0 open bugs, 64 fixed, 15 false positives documented
e6qu added a commit that referenced this pull request Mar 22, 2026
…witching (#33)

* fix: plugin hook error handling (B58-B59) + BUGS.md cleanup

B58: pluginGuard() now catches Plugin.trigger() errors and returns
EditResult error instead of propagating uncaught exception.

B59: pluginNotify() now catches Plugin.trigger() errors and logs
warning instead of silently ignoring.

BUGS.md cleaned up:
- Removed duplicate "Open — Bugs (0)" section
- Added B58-B59 as fixed
- Added 10 new false positives from QA analysis (V1, R4, E1-fork,
  PM1, PM2, etc.) — each verified with reasoning
- Updated Q3 reference from "this PR" to "PR #31"
- Total: 0 open bugs, 64 fixed, 15 false positives documented

* docs: QA round 3 — zero new bugs, 5 more false positives verified

Third round of deep analysis covering: session management, compaction,
prompt pipeline, skill/scripts, command templates, truncation.

All areas clean. All previous fixes (B48, B38, B57) verified holding.

New false positives (5):
- updatePart() orphaned parts → FK constraint prevents
- Script paths with spaces → array-based execution is safe
- Truncation boundary at maxBytes → comparison is correct
- Compaction during prompt → BusyError prevents
- filterEdited + sweep same part → orthogonal concerns

Total: 0 open bugs, 20 verified false positives.

* fix: B60 objective markdown injection + sweep error logging + integration proof tests

B60 (Med): Objective text injected directly into system prompt markdown.
Newlines and markdown chars (backticks, headers) could break formatting.
Fix: escape newlines and markdown special chars before injection.

Sweep error logging: Database.transaction() in sweep() now wrapped in
try-catch with log.error() instead of silent failure.

Integration proof tests (3 new):
- PROOF: hide() removes content from LLM context, CAS preserves original
- PROOF: unhide() restores original content from CAS
- PROOF: mark discardable + sweep removes content after N turns

Each test creates a real session with messages, performs the edit operation,
then verifies the content IS present before and IS NOT present after
(or vice versa for unhide). CAS storage verified independently.

Cross-module false positives documented (3 new):
- Protected message window timing → benign race
- Side thread system prompt staleness → fresh DB query per prompt
- Sweep transaction failure → now logged

tmux TUI tests: all 5 flows pass including LLM submit-message and cost-dialog.

* docs: add history editing prompts, slash commands, and verification guide

Added to docs/context-editing.md:
- Direct prompts to trigger context editing (hide, replace, externalize, mark, park)
- Complete slash command reference (/focus, /focus-rewrite-history, /btw, /reset-context, /classify, /threads, /history, /tree)
- How to enable focus agents in opencode.json config
- How history editing is verified (integration proof tests)

* fix: QA rounds 5-6 — B61-B64 fixes, 10 tmux flows, promptable agent switching

- B61: MCP add() inconsistent return type (Status vs Record) — all branches now return Record
- B62: text part timing start overwritten at stream end — preserve original start
- B63: unguarded JSON.parse on ripgrep output — flatMap with try-catch
- B64: untracked file line count off-by-one — trimEnd before split
- 5 new tmux test flows (slash-command, multi-agent-verify, slash-classify, slash-threads, slash-history) — 10 total
- Promptable agent mode switching: updated plan_enter/plan_exit tool descriptions for autonomous back-and-forth switching
- Documented mode switching flow in docs/agents.md
- 23 new false positives verified (49 total in BUGS.md)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant